﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

using InformationExtraction.Common;
using InformationExtraction.Interface;

namespace InformationExtraction.Utils
{
    //lz
    class NameExtracter : IFeatureExtractable
    {

        string[] nums = { "first" , "second" , "third" , "fourth" ,  "fifth" , "sixth" ,
                    "seventh","eighth","ninth","tenth","eleventh","thirteenth","fourteenth",
                "fifteenth","nineteenth","1st","2nd","3rd","xi","xii","xiii","xvii","xv","xvi"};

        string[] keywords = {"international", "conference", "symposium", "workshop"};

        string[] stops = { "and", "on", "of", "in" };

        public static int NAMELENGTH = 99;

        public NameExtracter()
        {           
        }

        /// <summary>
        /// 是否是处于文档前端的连续大写字母开头或全为大写的单词中
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        private int GetContinueCaseFeature(Token token)
        {
            if (token.InLine.No < 5 || token.InLine.No > 20)
                return -1;

            bool flag = false;

            if (IsPatialUpperCase(token.Content) || IsStopWords(token.Content) || IsOrdinalNumbers(token.Content))
            {
                Token tmp = token.Prior;
                int nPre = 0;
                int nPost = 0;

                for (int i = 0; i < 6 && tmp != null; i++)
                {
                    if (IsKeyWord(tmp.Content) || IsOrdinalNumbers(tmp.Content) || IsStopWords(tmp.Content))
                        flag = true;

                    if (IsPatialUpperCase(tmp.Content) || IsStopWords(tmp.Content) || IsOrdinalNumbers(token.Content))
                    {
                        nPre++;
                        tmp = tmp.Prior;
                    }
                }

                tmp = token.Next;

                for (int i = 0; i < 6 && tmp != null; i++)
                {
                    if (IsKeyWord(tmp.Content) || IsOrdinalNumbers(tmp.Content) || IsStopWords(tmp.Content))
                        flag = true;

                    if (IsPatialUpperCase(tmp.Content) || IsStopWords(tmp.Content) || IsOrdinalNumbers(token.Content))
                    {
                        nPost++;
                        tmp = tmp.Next;
                    }
                }

                if (nPost + nPre > 4 && flag)
                    return 1;
            }

            return -1;
        }

        private bool IsStopWords(string s)
        {
            string normal = s.ToLower().Trim();

            foreach (string stop in stops)
            {
                if (normal.Equals(stop))
                    return true;
            }

            return false;
        }

        private bool IsKeyWord(string s)
        {
            string normal = s.ToLower().Trim();

            foreach (string p in keywords)
            {
                if (normal.Equals(p))
                    return true;
            }

            return false;
        }

        private int GetNamePatternFeature(Token token)
        {
            Line line = token.InLine;

            int nOrdinalNumber = 0;
            int nKeywords = 0;

            foreach (Token t in line.Tokens)
            {
                if (IsKeyWord(t.Content) && IsPatialUpperCase(t.Content))
                    nKeywords++;

                if (IsOrdinalNumbers(t.Content))
                    nOrdinalNumber++;
            }

            if (nKeywords + nOrdinalNumber > 2)
                return 1;

            return -1;

            /*
            foreach (Token t in line.Tokens)
            {
                
                if (IsOrdinalNumbers(t.Content))
                    nOrdinalNumber++;

                if (IsKeyWord(t.Content))
                    nKeywords++;

                if (IsPatialUpperCase(t.Content))
                    nUppers++;

                if (t.Content.ToLower().Equals("on"))
                    onFlag = true;

                if (t.Content.ToLower().Equals("in"))
                    inFlag = true;

                if (t.Content.ToLower().Trim().Equals("call") || t.Content.ToLower().Trim().Equals("paper")
    || t.Content.Contains('.') || t.Content.Contains(',') || t.Content.Contains(':'))
                    badFlag = true;


            }

            if (badFlag)
                return -1;

            if (IsPatialUpperCase(token.Content) || token.Content.ToLower().Equals("on") || token.Content.ToLower().Equals("in"))
            {
                if (nKeywords > 0 && ((float)nUppers > 0.3f * (float)line.TokenCount || IsOrdinalNumbers(token.Content)) )
                {
                    if (line.TokenCount <= 2)
                    {
                        Line preLine = line.PriorLine;
                        if (preLine != null && preLine.TokenCount > 0 && GetNamePatternFeature(preLine.Tokens[0]) == 1)
                        {
                            return 1;
                        }

                        return -1;
 
                    }

                    return 1;
                }
                
            }
            

            return -1;
        */

        }

        private int GetOrdinalNumberFeature(Token token)
        {
            if (IsOrdinalNumbers(token.Content.ToLower().Trim()))
                return 1;

            return -1;
        }

        private int GetSurroundingOrdinalNumberFeature(Token token)
        {
            Line line = token.InLine;

            foreach (Token t in line.Tokens)
            {
                if (IsOrdinalNumbers(t.Content))
                    return 1;
            }

            return -1;
        }

        /// <summary>
        /// 是否位于文本的前端 
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        private int GetLocationFeature(Token token)
        {
            if (token.InLine.No > 4 && token.InLine.No < 20)
                return 1;

            return -1;

        }

        /// <summary>
        /// 当前单词是否是会议名称的关键词
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        private int GetKeywordsFeature(Token token)
        {
            return IsKeyWord(token.Content) ? 1 : -1;
        }


        /// <summary>
        /// 当前单词是否含有Keywords
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        private int GetSurroundingsKeywordFeaure(Token token)
        {
            Line line = token.InLine;
            for (int i = 0; i < 2 && line != null; i++)
            {
                foreach (Token t in line.Tokens)
                {
                    if (IsKeyWord(t.Content))
                        return 1;
                }

                line = line.PriorLine;
            }

            line = token.InLine.NextLine;
            for (int i = 0; i < 1 & line != null; i++)
            {
                foreach (Token t in line.Tokens)
                {
                    if (IsKeyWord(t.Content))
                        return 1;
                }

                line = line.NextLine;
            }

            return -1;
        }



        /// <summary>
        /// 当前单词是否为大写
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        private int GetCaseFeature(Token token)
        {
            return IsPatialUpperCase(token.Content) ? 1 : -1;
        }

        /// <summary>
        /// 当前单次是否为首字母大写
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        private int GetFirstLetterCaseFeature(Token token)
        {
            return IsFirstLetterUpperCase(token.Content) ? 1 : -1;
        }

        /// <summary>
        /// 附近的单词是否均为为大写字母开头
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        private int GetSurroundingsFirstLetterCaseFeature(Token token)
        {
            Line line = token.InLine;
            int nFirstUpperLetter = 0;
            foreach (Token t in line.Tokens)
            {
                if (IsFirstLetterUpperCase(token.Content))
                {
                    nFirstUpperLetter++;
                }
            }

            if ((float)nFirstUpperLetter / token.InLine.TokenCount > 0.5f)
                return 1;

            return -1;
        }

        /// <summary>
        /// 附近的单词是否全为大写
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        private int GetSurroundingsCaseFeature(Token token)
        {
            Line line = token.InLine;
            int nUppers = 0;

            foreach (Token t in line.Tokens)
            {
                if (IsPatialUpperCase(t.Content))
                    nUppers++;
            }

            if (nUppers > (float)line.TokenCount / 2.0f)
                return 1;

            return -1;
        }

        /// <summary>
        /// 是否满足 XXXXXXXXXX on XXXXXXXX XXXXXXXX 句式
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        private int GetOnPatternFeature(Token token)
        {
            Regex reg = new Regex(@"^[A-Z][a-zA-Z]+$");
            if (!reg.Match(token.Content).Success)
                return -1;

            Token tmp = FindToken(token, "on", true, 3);
            if (tmp != null)
            {
                int c = GetSurroundingsCaseFeature(tmp);
                return c;
            }
            else
            {
                tmp = FindToken(token, "on", false, 3);
                if (tmp != null)
                {
                    int c = GetSurroundingsCaseFeature(tmp);
                    return c;
                }
            }

            return -1;
        }


        private Token FindToken(Token token, string pattern, bool isForward, int maxBound)
        {
            Token tmp = null;
            int nCount = 0;
            if (isForward)
            {
                tmp = token.Next;
                while (tmp != null && nCount < maxBound)
                {
                    if (tmp.Content.ToLower().Trim().Equals(pattern))
                        break;
                    tmp = tmp.Next;
                    nCount++;
                }
                return tmp;
            }
            else
            {
                tmp = token.Prior;
                while (tmp != null && nCount < maxBound)
                {
                    if (tmp.Content.ToLower().Trim().Equals(pattern))
                        break;

                    tmp = tmp.Prior;
                    nCount++;
                }
                return tmp; 
            }
        }

        private Token FindTokenInLine(Token token, string pattern)
        {
            Line line = token.InLine;

            foreach (Token t in line.Tokens)
            {
                if (t.Content.ToLower().Trim().Equals(pattern))
                    return t;
            }

            return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        private int GetOfPatternFeature(Token token)
        {
            Regex reg = new Regex(@"^[A-Z][a-zA-Z]+$");
            if (!reg.Match(token.Content).Success)
                return -1;

            Token tmp = FindToken(token, "of", true, 3);
            if (tmp != null)
            {
                int c = GetSurroundingsCaseFeature(tmp);
                return c;
            }
            else
            {
                tmp = FindToken(token, "of", false, 3);
                if (tmp != null)
                {
                    int c = GetSurroundingsCaseFeature(tmp);
                    return c;
                }
            }

            return -1;
        }

        private int GetCFPFeature(Token token)
        {
            if (token.InLine.RawContent.ToUpper().Trim().Contains("CFP"))
                return 1;
            if (token.InLine.RawContent.ToLower().Trim().Contains("call for paper"))
                return 1;
            return -1;
        }


        //----------------------------------------------------------------------------------------------

        private bool IsOrdinalNumbers(string s)
        {
            string normal = s.ToLower().Trim();

            Regex reg = new Regex(@"^[1-9][0-9]?th$");
            Match mc = reg.Match(normal);

            if (mc.Success)
                return true;

            foreach (string num in nums)
            {
                if (normal.Equals(num))
                    return true;
            }
            return false;
        }

        private bool IsYear(string s)
        {
            string normal = s.ToLower().Trim();

            Regex reg = new Regex(@"^19[0-9][0-9]$");
            Regex reg2 = new Regex(@"^20[0-1][0-9]$");

            return reg.Match(s).Success || reg2.Match(s).Success;
        }

        private bool ContainsOneInWordSet(string s, string[] set)
        {
            string normal = s.ToLower().Trim();

            foreach (string pattern in set)
            {
                if (s.Equals(pattern))
                    return true;
            }

            return false;
        }

        private bool IsFirstLetterUpperCase(string s)
        {
            string normal = s.Trim();

            Regex reg = new Regex(@"^[A-Z][a-z]*$");
            Match mc = reg.Match(normal);

            return mc.Success;
        }

        private bool IsUpperCase(string s)
        {
            Regex reg = new Regex(@"^[A-Z]+$");
            Match mc = reg.Match(s);
            return mc.Success;
        }

        private bool IsPatialUpperCase(string s)
        {
            Regex reg = new Regex(@"^[A-Z][a-zA-Z]*$");
            Match mc = reg.Match(s);
            return mc.Success;
        }

        /// <summary>
        /// 判断同一行内是否包含序数词（表示会议届数）
        /// </summary>
        /// <param name="text"></param>
        /// <returns>是返回1，不是返回-1</returns>
        private int GetNearOrdinalFeature(string text, string line)
        {
            line = line.ToLower();
            Regex blank = new Regex("[ ]+");
            string[] lines = blank.Split(line);
            string[] nums = { "first" , "second" , "third" , "fourth" ,  "fifth" , "sixth" ,
                    "seventh","eighth","ninth","tenth","eleventh","thirteenth","fourteenth",
                "fifteenth","nineteenth","1st","2nd","3rd","xi","xii","xiii","xvii","xv","xvi"};
            bool flag = false;
            Regex r = new Regex("[0-9][0-9]?th");
            Match mc;
            int i;
            string word;
            for (i = 0; i < lines.Length; i++)
            {//找到序数词
                word = lines[i];
                foreach (string num in nums)
                {
                    if (word.Contains(num))
                    {
                        flag = true;
                        break;
                    }
                }
                mc = r.Match(word);
                if (mc.Success && mc.Value.Contains(word))
                {
                    flag = true;
                    break;
                }
            }
            string lowtext = text.ToLower();
            int j;
            for (j = 0; j < lines.Length; j++)
            {//找到原词位置
                if (lines[j].Equals(lowtext))
                    break;
            }
            if (Math.Abs(i - j) < NAMELENGTH && flag)
                return 1;
            else return -1;
        }


        /// <summary>
        /// 判断text是否位于一串以大写字母开头的单词中间
        /// </summary>
        /// <param name="text"></param>
        /// <param name="line"></param>
        /// <returns>是返回1，不是返回-1</returns>
        private int GetInUppersCaseFeature(string text, string line)
        {
            Regex blank = new Regex("[ ]+");
            string[] lines = blank.Split(line);
            Regex r = new Regex("[A-Z].*");
            Match mc;
            string word;
            bool flag1, flag2;
            //判断每个单词
            for (int i = 0; i < lines.Length; i++)
            {
                word = lines[i];
                if (text.Equals(word) == false)
                    continue;

                //查找这个词前后的词                
                if (i - 1 >= 0)
                {//前一个词
                    mc = r.Match(lines[i - 1]);
                    flag1 = mc.Success && mc.Value.Equals(lines[i - 1]);
                }
                else flag1 = true;
                if (i + 1 < lines.Length)
                {//后一个词
                    mc = r.Match(lines[i + 1]);
                    flag2 = mc.Success && mc.Value.Equals(lines[i + 1]);
                }
                else flag2 = true;

                mc = r.Match(text);
                if (mc.Success && mc.Value.Equals(text))
                {//text是以大写字母开头的，则前或后的词以大写字母开头即可
                    if (flag1 || flag2)
                    {
                        return 1;
                    }
                }
                else
                {//text不是以大写字母开头的，则必须前后的词都是以大写字母开头的
                    if (flag1 && flag2)
                    {
                        return 1;
                    }
                }
            }
            return -1;
        }


        /// <summary>
        /// 判断该单词是否全由小写字母组成
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        private int GetLowerCaseFeature(string text)
        {
            Regex r = new Regex("[a-z]+");
            Match mc;
            mc = r.Match(text);
            if (mc.Success && mc.Value.Equals(text))
                return -1;
            else return 1;
        }


        /// <summary>
        /// 判断该单词是否为混合数字字母的形式
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        private int GetMixNumberFeature(string text)
        {
            text = text.ToLower();
            Regex r1 = new Regex("[a-z]");
            Regex r2 = new Regex("[0-9]");
            Match mc1 = r1.Match(text);
            Match mc2 = r2.Match(text);
            if (mc1.Success && mc2.Success)
                return 1;
            else return -1;
        }

        #region IFeatureExtractable Members

        public List<int> Extract(Token token)
        {
            List<int> vec = new List<int>();

            vec.Add(GetContinueCaseFeature(token));
            vec.Add(GetKeywordsFeature(token));
            vec.Add(GetSurroundingsKeywordFeaure(token));
            vec.Add(GetNamePatternFeature(token));
            vec.Add(GetCaseFeature(token));
            vec.Add(GetFirstLetterCaseFeature(token));
            vec.Add(GetSurroundingsCaseFeature(token));
            vec.Add(GetSurroundingOrdinalNumberFeature(token));          
            vec.Add(GetOnPatternFeature(token));
            vec.Add(GetOfPatternFeature(token));
            vec.Add(GetLocationFeature(token));
            vec.Add(GetLowerCaseFeature(token.Content));
            vec.Add(GetCFPFeature(token));
            vec.Add(GetMixNumberFeature(token.Content));
            vec.Add(GetNearOrdinalFeature(token.Content, token.InLine.Content));
            vec.Add(GetInUppersCaseFeature(token.Content, token.InLine.Content));


            return vec;
        }

        public InformationType GetTargetType()
        {
            return InformationType.Name;
        }

        #endregion
    }
}
